package Grouping; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Map.Entry; import org.jsoup.safety.Cleaner; import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader.Array; import Builder.BrickGroupForTransform; import Command.LDrawColorT; import Command.LDrawPart; import Common.Vector3f; import Connectivity.Direction6T; import Connectivity.GlobalConnectivityManager; import LDraw.Files.LDrawStep; import LDraw.Files.LDrawStepRotationT; import LDraw.Support.ColorLibrary; import LDraw.Support.LDrawDirective; import LDraw.Support.MatrixMath; import LDraw.Support.type.LDrawGridTypeT; import Window.MOCBuilder; public class GroupingManager { private static GroupingManager _instance = null; private ArrayList<LDrawPart> cachedInstruction = null; private GroupingManager() { } public synchronized static GroupingManager getInstance() { if (_instance == null) _instance = new GroupingManager(); return _instance; } public void doGrouping() { for (LDrawStep step : MOCBuilder.getInstance().getWorkingLDrawFile() .activeModel().steps()) doGrouppingByBoundingBox(step); MOCBuilder.getInstance().removeEmptyStep(); preprocess(true); ArrayList<LDrawPart> sequenceList = getInstruction(); for (LDrawStep step : MOCBuilder.getInstance().getWorkingLDrawFile() .activeModel().steps()) doGroupingByColor(step); MOCBuilder.getInstance().removeEmptyStep(); mergingSteps(); adjustSequenceOfSteps(sequenceList); MOCBuilder.getInstance().removeEmptyStep(); preprocess(true); } private void doGrouppingByBoundingBox(LDrawStep step) { ArrayList<LDrawPart> allParts = new ArrayList<LDrawPart>(); if (step == null) return; if (step.stepRotationType() != LDrawStepRotationT.LDrawStepRotationNone) return; for (LDrawDirective directive : step.subdirectives()) if (directive instanceof LDrawPart) allParts.add((LDrawPart) directive); if (allParts.size() == 0) return; GlobalConnectivityManager connectivityManager = GlobalConnectivityManager .getInstance(); ArrayList<LDrawPart> newGroup = new ArrayList<LDrawPart>(); ArrayList<LDrawPart> waitingGroup = new ArrayList<LDrawPart>(); while (allParts.size() != 0) { newGroup.clear(); waitingGroup.clear(); waitingGroup.add(allParts.get(0)); while (waitingGroup.size() != 0) { LDrawPart part = waitingGroup.remove(0); newGroup.add(part); for (LDrawPart adjacentPart : connectivityManager .getAdjacentPartList(part.boundingBox3())) { if (newGroup.contains(adjacentPart) || waitingGroup.contains(adjacentPart) || allParts.contains(adjacentPart) == false) continue; waitingGroup.add(adjacentPart); } } allParts.removeAll(newGroup); makeAGroup(newGroup); } } public void doGrouping(LDrawStep step) { ArrayList<LDrawPart> sequenceList = getInstruction(); doGroupingByColor(step); MOCBuilder.getInstance().removeEmptyStep(); mergingSteps(); adjustSequenceOfSteps(sequenceList); MOCBuilder.getInstance().removeEmptyStep(); preprocess(true); } private void mergingSteps() { preprocess(true); // merge two steps if there exist hole to stud connection and stud to // hole connection. for (LDrawStep step : steps) { for (LDrawStep otherStep : steps) { if (step == otherStep) continue; boolean needMerge = false; Boolean hasStudToHoleConnection = null; for (LDrawPart part : partListPerStepMap.get(step)) { for (ConnectionPoint point : connectionPointPerPartMap .get(part)) { LDrawPart cPart = point.getTo().getConnectivity() .getParent(); if (point.isStudToHoleConnection() == null) continue; if (cPart.enclosingStep() != otherStep) continue; if (hasStudToHoleConnection == null) hasStudToHoleConnection = point .isStudToHoleConnection(); else if (hasStudToHoleConnection != point .isStudToHoleConnection()) { needMerge = true; break; } } if (needMerge) break; } if (needMerge == true) { System.out.println(step.browsingDescription() + "->" + otherStep.browsingDescription()); for (LDrawPart part : partListPerStepMap.get(step)) MOCBuilder.getInstance() .ChangeDirectivesParentStepAction(part, part.enclosingStep(), otherStep); preprocess(true); break; } } } } private void adjustSequenceOfSteps(ArrayList<LDrawPart> sequenceList) { ArrayList<LDrawStep> stepList = new ArrayList<LDrawStep>(); for (LDrawPart part : sequenceList) { if (stepList.contains(part.enclosingStep()) == false) stepList.add(part.enclosingStep()); } for (int i = 0; i < stepList.size(); i++) MOCBuilder.getInstance().changeStepIndex(stepList.get(i), i); } /* * �� �������� �ɰ� ����. */ private void doGroupingByColor(LDrawStep step) { ArrayList<LDrawPart> allParts = null; if (step == null) return; if (step.stepRotationType() != LDrawStepRotationT.LDrawStepRotationNone) return; allParts = partListPerStepMap.get(step); if (allParts == null || allParts.size() == 0) return; // 1. search major colors final HashMap<LDrawColorT, Float> colorVolumeMap = new HashMap<LDrawColorT, Float>(); for (LDrawPart part : allParts) { LDrawColorT partColor = part.getLDrawColor().getColorCode(); Vector3f size = part.boundingBox3().getMax() .sub(part.boundingBox3().getMin()); float volume = (float) Math.sqrt(size.x * size.y * size.z); if (colorVolumeMap.containsKey(partColor) == false) colorVolumeMap.put(partColor, volume); else colorVolumeMap.put(partColor, colorVolumeMap.get(partColor) + volume); } ArrayList<LDrawPart> remainingParts = new ArrayList<LDrawPart>(allParts); Collections.sort(remainingParts, Collections.reverseOrder(new Comparator<LDrawPart>() { @Override public int compare(LDrawPart o1, LDrawPart o2) { return colorVolumeMap.get( o1.getLDrawColor().getColorCode()).compareTo( colorVolumeMap.get(o2.getLDrawColor() .getColorCode())); } })); // 2. separate bricks having similar colors to a new group. ArrayList<LDrawPart> newGroup = new ArrayList<LDrawPart>(); ArrayList<LDrawPart> waitingList = new ArrayList<LDrawPart>(); while (remainingParts.size() != 0) { newGroup.clear(); waitingList.add(remainingParts.get(0)); while (waitingList.size() != 0) { LDrawPart part = waitingList.remove(0); newGroup.add(part); for (LDrawPart subPart : GlobalConnectivityManager .getInstance().getDirectlyConnectedParts(part)) { if (remainingParts.contains(subPart) == false || newGroup.contains(subPart) || waitingList.contains(subPart)) continue; if (subPart.getLDrawColor().getColorCode() .getDistance(part.getLDrawColor().getColorCode()) > 20000) continue; waitingList.add(subPart); } } // 2.1 add bricks connected to only the new group to the new group. if (newGroup.size() > 1) { ArrayList<LDrawPart> connList = new ArrayList<LDrawPart>(); while (true) { connList.clear(); connList.addAll(getConnectedPartListOnlyToTheGroup( newGroup, allParts)); int t = 0; for (LDrawPart subPart : connList) if (newGroup.contains(subPart) == false) { newGroup.add(subPart); t++; } if (t == 0) break; } makeAGroup(newGroup); } remainingParts.removeAll(newGroup); } // 3. separates connected bricks into new group. doGroupingByConnection(step); } /* * ���� �Ʒ� ���̺��� ���� ���� ���̱��� ��� �������� �Ѵܰ辿 �����鼭 ������ �����̽��ؼ� �����ຸ��. */ private void doGroupingFromBottomtoTop(LDrawStep step) { ArrayList<LDrawPart> allParts = new ArrayList<LDrawPart>(); if (step == null) return; if (step.stepRotationType() != LDrawStepRotationT.LDrawStepRotationNone) return; for (LDrawDirective directive : step.subdirectives()) if (directive instanceof LDrawPart) allParts.add((LDrawPart) directive); if (allParts.size() == 0) return; ArrayList<LDrawPart> remainingParts = new ArrayList<LDrawPart>(allParts); ArrayList<LDrawPart> newGroup = new ArrayList<LDrawPart>(); float height = Float.MIN_VALUE; for (LDrawPart part : remainingParts) if (height < part.boundingBox3().getMax().y) height = part.boundingBox3().getMax().y; while (remainingParts.size() != 0) { newGroup.clear(); for (LDrawPart part : remainingParts) { if (part.boundingBox3().getMin().y >= height) newGroup.add(part); } ArrayList<LDrawPart> tempList = new ArrayList<LDrawPart>(); for (LDrawPart part : newGroup) { for (LDrawPart subPart : getConnectedPartListExceptCustom2d( part, remainingParts)) if (tempList.contains(subPart) == false) tempList.add(subPart); } newGroup.addAll(tempList); makeAGroup(newGroup); remainingParts.removeAll(newGroup); height -= LDrawGridTypeT.Fine.getYValue(); } } private void doGroupingByConnection(LDrawStep step) { ArrayList<LDrawPart> allParts = new ArrayList<LDrawPart>(); if (step == null) return; if (step.stepRotationType() != LDrawStepRotationT.LDrawStepRotationNone) return; allParts = partListPerStepMap.get(step); if (allParts == null || allParts.size() == 0) return; ArrayList<LDrawPart> remainingParts = new ArrayList<LDrawPart>(allParts); ArrayList<LDrawPart> newGroup = new ArrayList<LDrawPart>(); while (remainingParts.size() != 0) { newGroup.clear(); LDrawPart part = remainingParts.get(0); newGroup.add(part); for (LDrawPart subPart : getConnectedPartList(part, remainingParts)) { if (remainingParts.contains(subPart) == false || newGroup.contains(subPart)) continue; newGroup.add(subPart); } // 3.1 add bricks connected to only the new group to the new group. if (newGroup.size() > 1) { ArrayList<LDrawPart> connList = new ArrayList<LDrawPart>(); while (true) { connList.clear(); connList.addAll(getConnectedPartListOnlyToTheGroup( newGroup, allParts)); int t = 0; for (LDrawPart subPart : connList) if (newGroup.contains(subPart) == false) { newGroup.add(subPart); t++; } if (t == 0) break; } makeAGroup(newGroup); } remainingParts.removeAll(newGroup); } } private void makeAGroup(ArrayList<LDrawPart> parts) { LDrawStep oldStep, newStep; MOCBuilder builder = MOCBuilder.getInstance(); newStep = builder.addStepToWorkingFile(); for (LDrawPart part : parts) { oldStep = part.enclosingStep(); builder.ChangeDirectivesParentStepAction(part, oldStep, newStep); } } private ArrayList<LDrawPart> getConnectedPartList(LDrawPart part, ArrayList<LDrawPart> partList) { ArrayList<LDrawPart> retList = new ArrayList<LDrawPart>(); ArrayList<LDrawPart> waitingList = new ArrayList<LDrawPart>(); ArrayList<LDrawPart> remainingParts = new ArrayList<LDrawPart>(partList); waitingList.add(part); remainingParts.remove(part); ArrayList<LDrawPart> connectedParts = new ArrayList<LDrawPart>(); while (waitingList.size() != 0) { connectedParts.clear(); part = waitingList.remove(0); retList.add(part); for (LDrawPart subPart : GlobalConnectivityManager.getInstance() .getDirectlyConnectedParts(part)) { if (remainingParts.contains(subPart) == false) continue; remainingParts.remove(subPart); if (waitingList.contains(subPart) == false) waitingList.add(subPart); } } return retList; } private ArrayList<LDrawPart> getConnectedPartListExceptCustom2d( LDrawPart part, ArrayList<LDrawPart> partList) { ArrayList<LDrawPart> retList = new ArrayList<LDrawPart>(); ArrayList<LDrawPart> waitingList = new ArrayList<LDrawPart>(); waitingList.add(part); while (waitingList.size() != 0) { part = waitingList.remove(0); retList.add(part); for (LDrawPart subPart : GlobalConnectivityManager.getInstance() .getDirectlyConnectedPartsExceptCustom2d(part)) { if (partList.contains(subPart) == false || retList.contains(subPart) || waitingList.contains(subPart)) continue; waitingList.add(subPart); } } return retList; } private ArrayList<LDrawPart> getConnectedPartListOnlyToTheGroup( ArrayList<LDrawPart> newGroup, ArrayList<LDrawPart> partList) { ArrayList<LDrawPart> retList = new ArrayList<LDrawPart>(); for (LDrawPart part : newGroup) { for (LDrawPart subPart : GlobalConnectivityManager.getInstance() .getDirectlyConnectedParts(part)) { if (partList.contains(subPart) == false || retList.contains(subPart)) continue; boolean isOnlyConnectedToNewGroup = true; for (LDrawPart subSubPart : GlobalConnectivityManager .getInstance().getDirectlyConnectedParts(subPart)) { if (newGroup.contains(subSubPart) == false) isOnlyConnectedToNewGroup = false; } if (isOnlyConnectedToNewGroup) retList.add(subPart); } } return retList; } private ArrayList<LDrawStep> steps; private HashMap<LDrawStep, ArrayList<LDrawPart>> partListPerStepMap; private HashMap<LDrawPart, ArrayList<ConnectionPoint>> connectionPointPerPartMap; public ArrayList<LDrawPart> getInstruction() { preprocess(); ArrayList<LDrawPart> retList = new ArrayList<LDrawPart>(); for (LDrawStep step : MOCBuilder.getInstance().getWorkingLDrawFile() .activeModel().steps()) if (partListPerStepMap.get(step) != null) if (partListPerStepMap.get(step).size() > 0) retList.addAll(getSequenceInStep(step)); return retList; } private ArrayList<LDrawPart> getSequenceInStep(LDrawStep step) { ArrayList<LDrawPart> retList = new ArrayList<LDrawPart>(); ArrayList<LDrawPart> bottomParts = getBottomPart(step); ArrayList<LDrawPart> waitingList = new ArrayList<LDrawPart>(); waitingList.addAll(bottomParts); retList.addAll(bottomParts); while (waitingList.size() != 0) { LDrawPart part = waitingList.remove(0); for (ConnectionPoint cPoint : connectionPointPerPartMap.get(part)) { LDrawPart cPart = cPoint.getTo().getConnectivity().getParent(); if (cPart.enclosingStep() == step && retList.contains(cPart) == false && waitingList.contains(cPart) == false) { waitingList.add(cPart); retList.add(cPart); } } } for (LDrawPart part : partListPerStepMap.get(step)) if (retList.contains(part) == false) retList.add(part); return retList; } private void preprocess() { preprocess(false); } private void preprocess(boolean update) { if (steps == null || update) { ArrayList<LDrawPart> partList = null; steps = MOCBuilder.getInstance().getWorkingLDrawFile() .activeModel().steps(); partListPerStepMap = new HashMap<LDrawStep, ArrayList<LDrawPart>>(); connectionPointPerPartMap = new HashMap<LDrawPart, ArrayList<ConnectionPoint>>(); for (LDrawStep step : steps) { partList = new ArrayList<LDrawPart>(); for (LDrawDirective directive : step.subdirectives()) if (directive instanceof LDrawPart) partList.add((LDrawPart) directive); partListPerStepMap.put(step, partList); ArrayList<ConnectionPoint> connectionPoint = null; for (LDrawPart part : partList) { connectionPoint = new ArrayList<ConnectionPoint>(); for (ConnectionPoint point : GlobalConnectivityManager .getInstance().getConnectionPoints(part)) { if (connectionPoint.contains(point)) continue; connectionPoint.add(point); } connectionPointPerPartMap.put(part, connectionPoint); } } } } private ArrayList<LDrawPart> getBottomPart(LDrawStep step) { preprocess(); ArrayList<LDrawPart> partList = null; if (step == null) { partList = MOCBuilder.getInstance().getAllPartInActiveModel(); } else partList = partListPerStepMap.get(step); ArrayList<LDrawPart> retPartList = new ArrayList<LDrawPart>(); LDrawPart retPart = null; for (LDrawPart part : partList) { if (isBottomPart(part, partList)) { if (retPart == null) { retPart = part; } else if (Math.round(part.boundingBox3().getMax() .sub(retPart.boundingBox3().getMax()).y) > 0) { retPart = part; retPartList.clear(); } else if (Math.round(part.boundingBox3().getMax() .sub(retPart.boundingBox3().getMax()).y) == 0) retPartList.add(part); } } if (retPart == null) { for (LDrawPart part : partList) { if (retPart == null) { retPart = part; } else if (Math.round(part.boundingBox3().getMax() .sub(retPart.boundingBox3().getMax()).y) > 0) { retPart = part; retPartList.clear(); } else if (Math.round(part.boundingBox3().getMax() .sub(retPart.boundingBox3().getMax()).y) == 0) retPartList.add(part); } } retPartList.add(retPart); return retPartList; } private boolean isBottomPart(LDrawPart part, ArrayList<LDrawPart> partList) { preprocess(); boolean isBottomBrick = false; for (ConnectionPoint connectionPoint : connectionPointPerPartMap .get(part)) { if (connectionPoint.isStudToHoleConnection() != null) { if (connectionPoint.isStudToHoleConnection() == true && connectionPoint.getFrom().getDirectionVector() .equals(new Vector3f(0, 1, 0))) isBottomBrick = true; else { if (partList.contains(connectionPoint.getTo() .getConnectivity().getParent())) { isBottomBrick = false; break; } } } } return isBottomBrick; } public void clear() { cachedInstruction = null; preprocess(true); } public void mergeAll() { preprocess(true); LDrawStep firstStep = null; for (LDrawStep step : MOCBuilder.getInstance().getWorkingLDrawFile() .activeModel().steps()) { if (firstStep == null) { firstStep = step; continue; } for (LDrawPart part : partListPerStepMap.get(step)) MOCBuilder.getInstance().ChangeDirectivesParentStepAction(part, part.enclosingStep(), firstStep); } MOCBuilder.getInstance().removeEmptyStep(); } }